﻿using System;
using Microsoft.Practices.Unity;
using TestByWire;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;

namespace TestByWire.Test
{
	using TestByWire.Test.Fakes;

	[TestClass]
	public class InvocationTests
	{
		[TestMethod]
		public void Construction_ReturnType()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();

			var invocation = Invocation.Create<IDbAccess>(x => x.GetCurrentSalary("Foo"), Priority.High);
			invocation.Returns(2);

			((IInvocationUpdater)invocation).Transfer(WrapInContainer(stub));

			Assert.AreEqual(2, stub.GetCurrentSalary("Foo"));
		}

		[TestMethod]
		public void Construction_WithNamedStub()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();

			var invocation = Invocation.Create<IDbAccess>(x => x.GetCurrentSalary("Foo"), "namedStub", Priority.High);
			invocation.Returns(2);

			((IInvocationUpdater)invocation).Transfer(WrapInContainer("namedStub", stub));

			Assert.AreEqual(2, stub.GetCurrentSalary("Foo"));
		}

		private static IUnityContainer WrapInContainer<T>(string name, T stub)
		{
			IUnityContainer container = new UnityContainer();
			container.RegisterInstance(name, stub);
			return container;
		}

		private static IUnityContainer WrapInContainer<T>(T stub)
		{
			return WrapInContainer(null, stub);
		}

		[TestMethod]
		public void Construction_RepeatOperation()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();

			var invocation = Invocation.Create<IDbAccess>(x => x.GetCurrentSalary("Foo"), Priority.High);
			invocation.Returns(2).Repeat(2);

			((IInvocationUpdater)invocation).Transfer(WrapInContainer(stub));

			Assert.AreEqual(2, stub.GetCurrentSalary("Foo"));
			Assert.AreEqual(2, stub.GetCurrentSalary("Foo"));
			Assert.AreEqual(0, stub.GetCurrentSalary("Foo"));
		}

		[TestMethod]
		public void ThrowOperation()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();

			var invocation = Invocation.Create<IDbAccess>(x => x.GetCurrentSalary("Foo"), Priority.High);
			var expectedException = new ArgumentException("Bar");
			invocation.Throws(expectedException);

			((IInvocationUpdater)invocation).Transfer(WrapInContainer(stub));

			try
			{
				stub.GetCurrentSalary("Foo");
				Assert.Fail("Mock should fail");
			}
			catch (Exception e)
			{
				Assert.AreEqual(e, expectedException);
			}

		}

		[TestMethod]
		public void Construction_ReturnTypeOnFunction()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();

			var invocation = Invocation.Create<IDbAccess>(x => x.GetCurrentSalary("Foo"), Priority.High);
			invocation.Returns(2);

			((IInvocationUpdater)invocation).Transfer(WrapInContainer(stub));

			Assert.AreEqual(2, stub.GetCurrentSalary("Foo"));
		}

		[TestMethod]
		public void Construction_VoidType()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();
			var invocation = Invocation.Create<IDbAccess>(x => x.StoreNewSalary("Foo", 3), Priority.High);

			((IInvocationUpdater)invocation).Transfer(WrapInContainer(stub));

			stub.StoreNewSalary("Foo", 3);

			stub.AssertWasCalled(x => x.StoreNewSalary("Foo", 3));
		}

		[TestMethod]
		public void ReturnValidOnVoidCall()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();
			var invocation = Invocation.Create<IDbAccess>(x => x.StoreNewSalary("Foo", 3), Priority.High);
			invocation.Returns(3);
			try
			{
				((IInvocationUpdater)invocation).Transfer(WrapInContainer(stub));
			}
			catch (InvalidOperationException)
			{
			}
		}

		[TestMethod]
		public void Create_ShouldStubProperty_WhenReturnTypeIsSpecified()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();
			var invocation = Invocation.Create<IDbAccess, string>(x => x.ConnectionString, Priority.High).Returns("Foo");

			((IInvocationUpdater)invocation).Transfer(WrapInContainer(stub));

			Assert.AreEqual("Foo", stub.ConnectionString);
		}

		[TestMethod]
		public void Create_ShouldStubPropertyWithSetter_WhenReturnTypeIsSpecified()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();
			var invocation = Invocation.Create<IDbAccess, string>(x => x.ProviderName, Priority.High).Returns("The Company");

			((IInvocationUpdater)invocation).Transfer(WrapInContainer(stub));

			Assert.AreEqual("The Company", stub.ProviderName);
		}

		[TestMethod]
		public void Create_ShouldStubMethod_WhenReturnTypeIsSpecified()
		{
			var stub = MockRepository.GenerateStub<IDbAccess>();

			var invocation = Invocation.Create<IDbAccess, int>(x => x.GetCurrentSalary("Foo"), Priority.High);
			invocation.Returns(2);

			((IInvocationUpdater)invocation).Transfer(WrapInContainer(stub));

			Assert.AreEqual(2, stub.GetCurrentSalary("Foo"));
		}
	}
}
